package com.clp.protocol.iec104.client;

import com.clp.protocol.iec104.client.async.MasterFuture;
import com.clp.protocol.iec104.client.async.MasterFutureListener;
import com.clp.protocol.iec104.client.async.MasterPromise;
import com.clp.protocol.iec104.client.config.MasterConfig;
import com.clp.protocol.iec104.client.event.MasterConnectEvent;
import com.clp.protocol.core.client.FailedToConnectException;
import com.clp.protocol.core.client.CouldNotAutoReconnectException;
import com.clp.protocol.core.event.EventPublisher;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;
import java.util.concurrent.TimeUnit;

@Slf4j
public class InMasterConnector {
    private final InMaster inMaster;
    private final ChannelInitializer<SocketChannel> initializer;
    @Getter
    private final EventPublisher<Master> eventPublisher;
    private final int maxReconnectCount;

    private volatile MasterFuture<Void> future;
    private volatile ConnectingState currState = ConnectingState.NONE;
    private volatile boolean isAllowAutoReconnect; // 断开时是否允许自动重连

    public InMasterConnector(InMaster inMaster, MasterConfig cfg) {
        this.inMaster = inMaster;
        this.initializer = new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                inMaster.registerChannel(ch);
            }
        };
        this.eventPublisher = new EventPublisher<>(inMaster.executor());
        this.maxReconnectCount = cfg.getControlConfig().getMaxReconnectCount();
    }

    /**
     * 如果是无状态，那么返回null；如果是当前状态，那么返回该状态的future
     *
     * @param state
     * @return
     */
    @Nullable
    private MasterFuture<Void> checkStateAndGetFuture(ConnectingState state) {
        if (currState == ConnectingState.NONE) {
            return null;
        }
        if (currState == state) {
            return future;
        }
        log.warn("[Connector] 当前状态：" + currState + ", 需要状态：" + state);
        throw new IllegalStateException("当前状态：" + currState + ", 需要状态：" + state);
    }

    private synchronized void setStateAndFuture(ConnectingState state, MasterFuture<Void> future) {
        this.currState = state;
        this.future = future;
    }

    /**
     * 发布连接事件
     *
     * @param cause 如果是失败事件，说明原因
     * @param type  连接事件的类型
     */
    private void publishConnectEvent(MasterConnectEvent.Type type, @Nullable Throwable cause) {
        eventPublisher.publishEvent(new MasterConnectEvent(inMaster, type, cause));
    }

    private void publishConnectEvent(MasterConnectEvent.Type type) {
        eventPublisher.publishEvent(new MasterConnectEvent(inMaster, type, null));
    }

    public synchronized MasterFuture<Void> connect() {
        if (inMaster.isConnected()) {
            throw new IllegalStateException("当前连接有效，无法再次进行连接！");
        }
        future = checkStateAndGetFuture(ConnectingState.CONNECTING);
        if (future != null) {
            return future;
        }

        log.info("[Connector] 即将进行连接:{}", inMaster);
        publishConnectEvent(MasterConnectEvent.Type.CONNECT_BEFORE);
        MasterPromise<Void> promise = inMaster.newPromise((Void) null);
        setStateAndFuture(ConnectingState.CONNECTING, promise);
        connect0(promise);
        return promise.addListener(new MasterFutureListener<Void>() {
            @Override
            public void operationComplete(MasterFuture<Void> future) {
                setStateAndFuture(ConnectingState.NONE, null);
                isAllowAutoReconnect = true;
                if (future.isSuccess()) {
                    publishConnectEvent(MasterConnectEvent.Type.CONNECT_SUCCESS);
                    log.info("[Connector] 连接成功:{}", inMaster);
                } else {
                    publishConnectEvent(MasterConnectEvent.Type.CONNECT_FAILED, future.cause());
                    log.info("[Connector] 连接失败:{}", inMaster);
                }
            }
        });
    }

    /**
     * 连接：会先进行一次连接，如果连接成功，返回成功结果；如果没有成功，则会一直重连，直到超过最大重连次数
     *
     * @return
     */
    private void connect0(MasterPromise<Void> promise) {
        inMaster.getNettyClient().connect(initializer, inMaster.remoteHost(), inMaster.remotePort()).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (future.isSuccess()) {
                    promise.setSuccess();
                    return;
                }

                log.info("[Connector] 连接失败, 原因：{}，尝试重新连接：{}", future.cause().getMessage(), inMaster);
                connect0Loop(promise, 1);
            }
        });
    }

    private void connect0Loop(MasterPromise<Void> promise, int reconnCount) {
        if (maxReconnectCount >= 0 && reconnCount >= maxReconnectCount) {
            promise.setFailure(new FailedToConnectException("[Connector] 超过最大重连次数:" + reconnCount + "，连接失败！"));
        }

        log.info("[Connector] 进行对 {}:{} 的第{}次重连", inMaster.remoteHost(), inMaster.remotePort(), reconnCount);
        inMaster.executor().schedule(() -> {
            inMaster.getNettyClient().connect(initializer, inMaster.remoteHost(), inMaster.remotePort()).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (future.isSuccess()) {
                        log.info("[Connector] 对 {}:{} 的第{}次重连成功", inMaster.remoteHost(), inMaster.remotePort(), reconnCount);
                        promise.setSuccess();
                        return;
                    }

                    connect0Loop(promise, reconnCount + 1);
                }
            });
        }, reconnCount, TimeUnit.SECONDS);
    }

    /**
     * @return
     * @throws CouldNotAutoReconnectException
     */
    public synchronized void tryAutoReconnect() {
        if (inMaster.isConnected() || !isAllowAutoReconnect) {
            log.info("[Connector] 当前不允许自动重连");
            return;
        }
        try {
            MasterFuture<Void> future = checkStateAndGetFuture(ConnectingState.AUTO_RECONNECTING);
            if (future != null) {
                return;
            }
        } catch (IllegalStateException e) {
            // 不应到达，如果到达了，则需要移除这个master
            // TODO
            return;
        }

        log.info("[Connector] 即将自动重连:{}", inMaster);
        publishConnectEvent(MasterConnectEvent.Type.AUTO_RECONNECT_BEFORE);
        MasterPromise<Void> promise = inMaster.newPromise((Void) null);
        setStateAndFuture(ConnectingState.AUTO_RECONNECTING, promise);
        connect0(promise);
        promise.addListener(new MasterFutureListener<Void>() {
            @Override
            public void operationComplete(MasterFuture<Void> future) {
                setStateAndFuture(ConnectingState.NONE, null);
                if (future.isSuccess()) {
                    publishConnectEvent(MasterConnectEvent.Type.AUTO_RECONNECT_SUCCESS);
                    log.info("[Connector] 自动重连成功:{}", inMaster);
                } else {
                    publishConnectEvent(MasterConnectEvent.Type.AUTO_RECONNECT_FAILED);
                    log.info("[Connector] 自动重连失败:{}", inMaster);
                }
            }
        });
    }

    /**
     * 客户端主动重连
     *
     * @return
     */
    public synchronized MasterFuture<Void> reconnect() {
        MasterFuture<Void> future = checkStateAndGetFuture(ConnectingState.RECONNECTING);
        if (future != null) {
            return future;
        }

        log.info("[Connector] 即将手动重连:{}", inMaster);
        publishConnectEvent(MasterConnectEvent.Type.RECONNECT_BEFORE);
        MasterPromise<Void> promise = inMaster.newPromise((Void) null);
        setStateAndFuture(ConnectingState.RECONNECTING, promise);
        if (inMaster.isConnected()) {
            MasterPromise<Void> disconnectPromise = inMaster.newPromise((Void) null);
            isAllowAutoReconnect = false;
            disconnect0(disconnectPromise);
            disconnectPromise.addListener(new MasterFutureListener<Void>() {
                @Override
                public void operationComplete(MasterFuture<Void> future) {
                    if (!future.isSuccess()) {
                        promise.setFailure(future.cause());
                        return;
                    }
                    connect0(promise);
                }
            });
        } else {
            connect0(promise);
        }

        return promise.addListener(new MasterFutureListener<Void>() {
            @Override
            public void operationComplete(MasterFuture<Void> future) {
                setStateAndFuture(ConnectingState.NONE, null);
                isAllowAutoReconnect = true;
                if (future.isSuccess()) {
                    publishConnectEvent(MasterConnectEvent.Type.RECONNECT_SUCCESS);
                    log.info("[Connector] 手动重连成功:{}", inMaster);
                } else {
                    publishConnectEvent(MasterConnectEvent.Type.RECONNECT_FAILED);
                    log.info("[Connector] 手动重连失败:{}", inMaster);
                }
            }
        });
    }

    public synchronized MasterFuture<Void> disconnect() {
        if (!inMaster.isConnected()) return inMaster.newPromise((Void) null).setSuccess();

        future = checkStateAndGetFuture(ConnectingState.DISCONNECTING);
        if (future != null) {
            return future;
        }

        log.info("[Connector] 即将断开连接:{}", inMaster);
        publishConnectEvent(MasterConnectEvent.Type.DISCONNECT_BEFORE);
        MasterPromise<Void> promise = inMaster.newPromise((Void) null);
        setStateAndFuture(ConnectingState.DISCONNECTING, promise);
        isAllowAutoReconnect = false;
        disconnect0(promise);
        return promise.addListener(new MasterFutureListener<Void>() {
            @Override
            public void operationComplete(MasterFuture<Void> future) {
                setStateAndFuture(ConnectingState.NONE, null);
                if (future.isSuccess()) {
                    publishConnectEvent(MasterConnectEvent.Type.DISCONNECT_SUCCESS);
                    log.info("[Connector] 断开连接成功:{}", inMaster);
                } else {
                    publishConnectEvent(MasterConnectEvent.Type.DISCONNECT_FAILED, future.cause());
                    log.info("[Connector] 断开连接失败:{}", inMaster);
                }
            }
        });
    }

    private void disconnect0(MasterPromise<Void> promise) {
        inMaster.channel().close().addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (future.isSuccess()) {
                    promise.setSuccess();
                } else {
                    promise.setFailure(future.cause());
                    future.cause().printStackTrace();
                }
            }
        });
    }

    private enum ConnectingState {
        NONE,
        CONNECTING,
        RECONNECTING,
        AUTO_RECONNECTING,
        DISCONNECTING,
        AUTO_DISCONNECTING
    }

}
